/* ----- resident.c ----- */

#include <dos.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "tsr.h"

static union REGS rg;
static struct SREGS seg;

/* --- vectors --- */
#define KEYBD           9
#define TIMER           0x1c
#define DISK            0x13
#define VIDEO           0x10
#define ZERODIV         0
#define INT28           0x28
#define CRIT            0x24
#define CTRLC           0x23
#define CTRLBRK         0x1b

/* --- interrupt vector chains --- */
static void interrupt (far *tmpvect)(void);
static void interrupt (far *oldbreak)(void);
static void interrupt (far *oldctrlc)(void);
 void interrupt (far *oldtimer)(void);
 void interrupt (far *old28)(void);
 void interrupt (far *oldkb)(void);
 void interrupt (far *olddisk)(void);
 void interrupt (far *oldvideo)(void);
static void interrupt (far *oldcrit)(void);
/* --- ISRs for the TSR --- */
extern void interrupt newtimer(void);
extern void interrupt new28(void);
extern void interrupt newkb(void);
extern void interrupt newdisk(void);
extern void interrupt newvideo(void);
extern void interrupt newcrit();
extern void interrupt newbreak();
/* --- global variables --- */
static unsigned sizeprogram;
static unsigned dosseg;
char far *dosbusy;
static char far *mydta;
unsigned myss;
unsigned stack;
static unsigned intpsp;
static unsigned far *psps[5];
static int pspctr;
static int resoff;
extern unsigned running;
int popflg;
int suspending=0;
int terminating=0;
int diskflag=0;
int videoflag=0;

/* --- local prototypes --- */
static void resterm(void);
static void pspaddr(void);
void dores(void);
void popup_tsr(void);
/* --- establish and declare residency --- */
void resinit()
{       int pp,xtmp;
        suspending=0;terminating=0;running=0;
        segread(&seg);
        myss = seg.ss;
        stack = getsp();
        /* --- get address of DOS busy flag --- */
        rg.h.ah = 0x34;
        intdosx(&rg,&rg,&seg);
        dosseg = seg.es;
        FP_SEG(dosbusy)=seg.es;
        FP_OFF(dosbusy)=rg.x.bx;
        /* --- get address of resident program's dta --- */
        mydta = getdta();
        pspaddr();
        /* --- get original interrupt vectors --- */
        oldtimer = getvect(TIMER);
        old28 = getvect(INT28);
        oldkb = getvect(KEYBD);
        olddisk = getvect(DISK);
        oldvideo = getvect(VIDEO);

        /* --- attach vectors to resident program --- */
        tmpvect=newtimer;    setvect(TIMER,tmpvect);
        tmpvect=new28;       setvect(INT28,tmpvect);
        tmpvect=newkb;       setvect(KEYBD,tmpvect);
        tmpvect=newdisk;     setvect(DISK, tmpvect);
        tmpvect=newvideo;    setvect(VIDEO,tmpvect);

        /* --- compute program size --- */
        sizeprogram = farsetsize(0)+50;
        /* --- terminate and stay resident --- */
        keep(0,sizeprogram);
}

void resident_psp()
{
        int pp;
        intpsp = *psps[0];
        for (pp=0; pp<pspctr; pp++)
                *psps[pp]=_psp;
}

void interrupted_psp()
{
        int pp;
        for (pp=0; pp<pspctr; pp++)
                *psps[pp]=intpsp;
}

void popup_tsr()
{
        static char far *intdta;
        static unsigned intsp;
        static unsigned intss;
        static unsigned ctrl_break;

        disable();
        oldcrit=getvect(CRIT);
        oldbreak=getvect(CTRLBRK);
        oldctrlc=getvect(CTRLC);
        tmpvect=newcrit; setvect(CRIT,tmpvect);
        tmpvect=newbreak; setvect(CTRLBRK,tmpvect);
        setvect(CTRLC,tmpvect);
        ctrl_break=getcbrk();
        setcbrk(0);
        intdta=getdta();
        setdta(mydta);
        resident_psp();
        enable();
        popup();
        disable();
        interrupted_psp();
        setdta(intdta);
        setvect(CRIT,oldcrit);
        setvect(CTRLBRK,oldbreak);
        setvect(CTRLC,oldctrlc);
        setcbrk(ctrl_break);
        enable();

        if (terminating) terminate();
}

unsigned resident(char *signature)
{
        char *sg;
        unsigned df,mcbseg,blkseg;

        segread(&seg);
        df=seg.ds-_psp;
        rg.h.ah = 0x52;
        intdosx(&rg,&rg,&seg);
        mcbseg=peek(seg.es,rg.x.bx-2);
        while (peekb(mcbseg,0)==0x4d) {
           blkseg=peek(mcbseg,1);
           if (peek(blkseg,0)==0x20cd) {
              if (blkseg==_psp)
                 break;
              for (sg=signature;*sg;sg++)
                 if (*sg!=peekb(blkseg+df,(unsigned)sg))
                    break;
              if (!*sg) {
                 if (peek(blkseg+df,(unsigned)&suspending)) {
                    poke(blkseg+df,(unsigned)&suspending,0);
                    return 2;
                 }
                 return 1;
              }
           }
           mcbseg+=peek(mcbseg,3)+1;
        }
        return 0;
}

static void pspaddr()
{
        unsigned adr=0;
        unsigned enddos;
        static unsigned far *ed, far *ps;
        adr=0;
        rg.h.ah=0x52;
        intdosx(&rg,&rg,&seg);
        FP_SEG(ed)=seg.es;
        FP_OFF(ed)=rg.x.bx-2;

        enddos=*ed;
        while(pspctr<5 &&
                    (unsigned)((dosseg<<4)+adr)<(enddos<<4)){
                FP_SEG(ps)=dosseg;
                FP_OFF(ps)=adr;
                if (*ps==_psp){
                        rg.h.ah = 0x50;
                        rg.x.bx = _psp+1;
                        intdos(&rg,&rg);
                        if (*ps==_psp+1)
                                psps[pspctr++]=ps;
                        rg.h.ah = 0x50;
                        rg.x.bx = _psp;
                        intdos(&rg,&rg);
                }
                adr++;
        }
}

static void resterm()
{
        static unsigned far *env;
        closefiles();
        setvect(TIMER,oldtimer);
        setvect(INT28,old28);
        setvect(KEYBD,oldkb);
        setvect(DISK,olddisk);
        setvect(VIDEO,oldvideo);
        FP_SEG(env)=_psp;
        FP_OFF(env)=0x2c;
        freemem(*env);
        freemem(_psp);
}

void terminate()
{
        tmpvect=newvideo;
        if (getvect(VIDEO)==tmpvect) {
                tmpvect=newdisk;
                if (getvect(DISK)==tmpvect) {
                        tmpvect=newkb;
                        if (getvect(KEYBD)==tmpvect) {
                                tmpvect=new28;
                                if (getvect(INT28)==tmpvect) {
                                        tmpvect=newtimer;
                                        if (getvect(TIMER)==newtimer){
                                                resterm();
                                                return;
                                        }
                                }
                        }
                }
        }
        terminating=0;
        suspending=1;
}


